/**
 * Company:wz
 * Title:NettyHelper.java
 * Description:
 * Date:2016年6月13日下午3:35:26
 * author:wei.liang 
 * Version:V1.0
 */

package com.wz.datachannel.netty;

import org.apache.log4j.Logger;

import com.wz.datachannel.interfaces.OnNettyClientManagerEvent;
import com.wz.datachannel.model.protobuf.WznetDpBase;
import com.wz.datachannel.util.log4j.Log4jManager;

import android.content.Context;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

/** 
 * Netty客户端服务
 * @author wei.liang 
 * @Date 2016年6月13日下午3:35:26
 */
public class NettyHelper {
	private Logger mLog = Log4jManager.getInstance();
	private Context mContext = null;
	private OnNettyClientManagerEvent mCallBack = null;

	private EventLoopGroup mWorkGroup;
	private Bootstrap mBootstrap;
	private Channel mChannel;
	//private ChannelFuture mChannelFuture;
	private String mChannelId;

	public NettyHelper(Context context, OnNettyClientManagerEvent callback) {
		this.mContext = context;
		this.mCallBack = callback;
		init();
	}

	private void init() {
		mWorkGroup = new NioEventLoopGroup();
		mBootstrap = new Bootstrap();
		mBootstrap.group(mWorkGroup);
		mBootstrap.channel(NioSocketChannel.class);
		mBootstrap.option(ChannelOption.TCP_NODELAY, true);
		mBootstrap.handler(new NettyClientInitializer());
	}

	public boolean isActive() {
		boolean result = false;
		try {
			result = mChannel.isActive();
		} catch (Exception e) {
		}

		return result;
	}

	/**
	 * 同步连接
	 * 
	 * @author wei.liang
	 * @return 连接结果
	 */
	public void doConnectSync(String ip, int port) {
		try {
			// 发起同步链接操作
			mLog.debug("loginSerAddr, " + ip + ":" + port);
			//			mChannelFuture = mBootstrap.connect(ip, port).await();
			//			mChannelId = mChannelFuture.channel().id().asShortText();

			ChannelFuture channelFuture = mBootstrap.connect(ip, port);//.sync();
			mChannel = channelFuture.awaitUninterruptibly().channel();
			//mChannelId = mChannel.id().asShortText();
			//mLog.debug("新channelId:" + mChannel.id().asShortText());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 异步连接
	 * 
	 * @author wei.liang
	 * @return 连接结果
	 */
	public void doConnectAsync(String ip, int port) {
		try {
			// 发起同步链接操作
			mLog.debug("loginSerAddr, " + ip + ":" + port);
			//mChannelFuture = mBootstrap.connect(ip, port);
			ChannelFuture channelFuture = mBootstrap.connect(ip, port);//.sync();
			mChannel = channelFuture.channel();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void close() {
		try {
			//mChannelFuture.cancel(true);
			mChannel.close();
			//mChannelFuture.channel().closeFuture().awaitUninterruptibly();
			//mChannel.closeFuture();
			//mChannel.close();
			//mWorkGroup.shutdownGracefully();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void sendData(Object msg) {
		if (isActive()) {
			mChannel.writeAndFlush(msg);
		}
	}

	class NettyClientInitializer extends ChannelInitializer<SocketChannel> {

		@Override
		protected void initChannel(SocketChannel ch) throws Exception {
			ChannelPipeline pipeline = ch.pipeline();
			pipeline.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
			pipeline.addLast("protobufDecoder", new ProtobufDecoder(WznetDpBase.wznet_dp_base.getDefaultInstance()));
			pipeline.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
			pipeline.addLast("protobufEncoder", new ProtobufEncoder());
			pipeline.addLast("idleStateHandler", new IdleStateHandler(20, 30, 0));
			pipeline.addLast("handler", new NettyClientHandler());
			mChannelId = ch.id().asShortText();
			//mLog.debug(mChannelId);
		}
	}

	class NettyClientHandler extends SimpleChannelInboundHandler<String> {

		@Override
		public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
			super.userEventTriggered(ctx, evt);
			if (!isEqualChannel(ctx, "READER_IDLE")) {
				return;
			}
			if (evt instanceof IdleStateEvent) {
				IdleStateEvent event = (IdleStateEvent) evt;
				if (event.state().equals(IdleState.READER_IDLE)) {// 超时未收到信息，发生心跳
					if (null != mCallBack) {
						mCallBack.onChannelIdle();
					}
				} else if (event.state().equals(IdleState.WRITER_IDLE)) {// 
				} else if (event.state().equals(IdleState.ALL_IDLE)) {
				}
			}
		}

		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
			super.exceptionCaught(ctx, cause);
			cause.printStackTrace();
			mLog.info("NettyClient - exceptionCaught.服务器连接异常");
			ctx.close();
		}

		@Override
		public void channelInactive(ChannelHandlerContext ctx) throws Exception {
			if (!isEqualChannel(ctx, "channelInactive")) {
				return;
			}
			mLog.info("NettyClient - channelInactive.服务器断开连接");
			super.channelInactive(ctx);
			try {
				if (null != mCallBack) {
					mCallBack.onConnectFailed();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}

		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			super.channelActive(ctx);
			if (!isEqualChannel(ctx, "channelActive")) {
				return;
			}
			mLog.info("NettyClient  -  连接服务器成功!");
			if (null != mCallBack) {
				mCallBack.onConnectSucceed();
			}
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			if (!isEqualChannel(ctx, "channelRead")) {
				return;
			}
			if (null != mCallBack) {
				mCallBack.onRcvData(msg);
			}
		}

		@Override
		protected void messageReceived(ChannelHandlerContext ctx, String arg1) throws Exception {
			mLog.debug(arg1);
		}
	}

	private synchronized boolean isEqualChannel(ChannelHandlerContext ctx, String tag) {
		boolean reuslt = false;
		try {
			String nCtxId = ctx.channel().id().asShortText();
			reuslt = mChannelId.equals(nCtxId);
		} catch (Exception e) {
		}
		if (!reuslt) {
			mLog.debug(tag + ",isEqualChannel  " + reuslt);
		}
		return reuslt;
	}
}
